package crmdna.client.member;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.google.gson.Gson;
import com.googlecode.objectify.ObjectifyFilter;

import crmdna.client.contactdetail.ContactDetail.ContactDetailProp;
import crmdna.client.group.Group;
import crmdna.client.group.Group.GroupProp;
import crmdna.client.member.InteractionCore.InteractionProp;
import crmdna.client.member.InteractionCore.InteractionType;
import crmdna.client.member.MemberCore.MemberProp;
import crmdna.client.practice.Practice;
import crmdna.client.practice.Practice.PracticeProp;
import crmdna.client.program.Program;
import crmdna.client.program.Program.ProgramProp;
import crmdna.client.programtype.ProgramType;
import crmdna.client.programtype.ProgramType.ProgramTypeProp;
import crmdna.client.teacher.Teacher;
import crmdna.client.teacher.Teacher.TeacherProp;
import crmdna.client.user.User;
import crmdna.client.user.User.Action;
import crmdna.client.user.User.ResourceType;
import crmdna.client.user.UserCore;
import crmdna.client.venue.Venue;
import crmdna.client.venue.Venue.VenueProp;
import crmdna.common.APIException;
import crmdna.common.APIResponse.Status;
import crmdna.common.OfyService;
import crmdna.crmdna.Client;
import crmdna.custom.isha.IshaConfig;

public class MemberTest {

	private final LocalServiceTestHelper datastoreHelper =
			new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()
			.setApplyAllHighRepJobPolicy());

	private final String client = "isha";
	private final String invalidClient = "invalid";
	private final String validUser = "valid@login.com";
	private final String validUser2 = "valid2@login.com";
	private final String invalidUser = "invalid@login.com";
	private final String userWithPermission = "userwithpermission@login.com";
	private GroupProp sgp;
	private GroupProp kl;
	private GroupProp sydney;
	private GroupProp iyc;

	private PracticeProp shambhavi, mysticEye, ishaKriya;

	private ProgramTypeProp shambhavi2Day, mysticEye1Day;

	ProgramProp shambhavi201405, mysticEye201401, sathsang201405;

	MemberProp thulasi;
	String sgpUser = "sgpuser@valid.com";

	@Before
	public void setUp() {
		datastoreHelper.setUp();
		ObjectifyFilter.complete();

		Client.create(client);
		sgp = Group.create(client, "Singapore", User.SUPER_USER);
		assertEquals(1, sgp.groupId);

		kl = Group.create(client, "Malaysia/KL", User.SUPER_USER);
		assertEquals(2, kl.groupId);

		sydney = Group.create(client, "Australia/Sydney", User.SUPER_USER);
		assertEquals(3, sydney.groupId);

		iyc = Group.create(client, "India/Isha Yoga Center", User.SUPER_USER);
		assertEquals(4, iyc.groupId);

		User.create(client, validUser, sgp.groupId, User.SUPER_USER);
		assertEquals(1, UserCore.get(client, validUser).toProp().userId);

		User.create(client, validUser2, sgp.groupId, User.SUPER_USER);
		assertEquals(2, UserCore.get(client, validUser2).toProp().userId);

		User.create(client, userWithPermission, sgp.groupId, User.SUPER_USER);
		assertEquals(3, UserCore.get(client, userWithPermission).toProp().userId);
		User.addOrDeletePrivilege(client, userWithPermission, ResourceType.INTERACTION, 
				0, Action.WRITE, true, User.SUPER_USER);


		shambhavi = Practice.create("isha", "Shambhavi", User.SUPER_USER);
		mysticEye = Practice.create("isha", "MysticEye", User.SUPER_USER);
		ishaKriya = Practice.create("isha", "IshaKriya", User.SUPER_USER);

		Set<Long> sathsangPracticeIds = new HashSet<>();
		sathsangPracticeIds.add(shambhavi.practiceId);
		sathsangPracticeIds.add(mysticEye.practiceId);

		Set<Long> practiceIds = new HashSet<>();
		practiceIds.add(shambhavi.practiceId);
		ProgramTypeProp shambhavi2Day = ProgramType.create("isha", "Shambhavi", 
				practiceIds, User.SUPER_USER);

		practiceIds.clear();
		practiceIds.add(mysticEye.practiceId);
		ProgramTypeProp mysticEye1Day = ProgramType.create("isha", "MysticEye", 
				practiceIds, User.SUPER_USER);

		ProgramTypeProp sathsang = ProgramType.create("isha", "Sathsang", null, User.SUPER_USER);

		GroupProp chennai = Group.create("isha", "Chennai", User.SUPER_USER);

		VenueProp giis = Venue.create("isha", "GIIS", "GIIS", sgp.groupId, User.SUPER_USER);
		VenueProp expo = Venue.create("isha", "Expo", "Expo", sgp.groupId, User.SUPER_USER);

		TeacherProp nidhi = Teacher.create("isha", "nidhi.jain@ishafoundation.org", sgp.groupId, 
				User.SUPER_USER);
		TeacherProp sadhguru = Teacher.create("isha", "sadhguru@ishafoundation.org", sgp.groupId, 
				User.SUPER_USER);

		shambhavi201405 = Program.create("isha", sgp.groupId, 
				shambhavi2Day.programTypeId, giis.venueId, nidhi.teacherId, 
				20140503, 20140504, 1, null, User.SUPER_USER);

		mysticEye201401 = Program.create("isha", sgp.groupId, 
				mysticEye1Day.programTypeId, expo.venueId, sadhguru.teacherId, 
				20140503, 20140504, 1, null, User.SUPER_USER);

		sathsang201405 = Program.create("isha", sgp.groupId, 
				sathsang.programTypeId, giis.venueId, nidhi.teacherId, 
				20140503, 20140503, 1, null, User.SUPER_USER);

		User.create("isha", sgpUser, sgp.groupId, User.SUPER_USER);
		User.addOrDeletePrivilege("isha", sgpUser, ResourceType.GROUP, 
				sgp.groupId, Action.WRITE, true, User.SUPER_USER);		    
	}

	@After
	public void tearDown() {       
		ObjectifyFilter.complete();
		datastoreHelper.tearDown();
	}

	@Test
	public void createTest() {
		//client should be valid
		try {
			Member.create(invalidClient, sgp.groupId, null, invalidUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
		}

		//should not be invalid user
		try {
			Member.create(client, sgp.groupId, null, invalidUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_INVALID_USER, ex.statusCode);
		}

		//group id should be valid
		try {
			Member.create(client, 100, null, validUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
		}

		//contact details should be valid
		ContactDetailProp contact = new ContactDetailProp();
		contact.firstName = "sathya"; //no email or phone number
		try {
			Member.create(client, sgp.groupId, contact, validUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_RESOURCE_INCORRECTLY_SPECIFIED, ex.statusCode);
		}

		contact.email = "sathya.t@ishafoundation.org";
		MemberProp prop = Member.create(client, sgp.groupId, contact, validUser);

		prop = MemberCore.safeGet(client, prop.memberId).toProp();
		assertEquals("sathya.t@ishafoundation.org", prop.contact.email);

		//cannot add the same email again
		try {
			Member.create(client, sgp.groupId, contact, validUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_RESOURCE_ALREADY_EXISTS, ex.statusCode);
		}

		//can add member without email but with valid phone number
		contact.email = null;
		contact.homePhone = "+6565227030";
		prop = Member.create(client, sgp.groupId, contact, validUser);

		prop = MemberCore.safeGet(client, prop.memberId).toProp();
		assertEquals("+6565227030", prop.contact.homePhone);
		assertEquals(null, prop.contact.email);    	    	    	    
	}

	@Test
	public void updateContactDetailsTest() {
		ContactDetailProp contact = new ContactDetailProp();
		contact.firstName = "sathya";
		contact.email = "sathya.t@ishafoundation.org";
		MemberProp prop = Member.create(client, sgp.groupId, contact, validUser);
		assertEquals(1, prop.memberId);

		//change first name
		contact = new ContactDetailProp();
		contact.firstName = "Sathyanarayanan";
		Member.updateContactDetails(client, prop.memberId, contact, validUser);
		prop = MemberCore.safeGet(client, prop.memberId).toProp();
		assertEquals("Sathyanarayanan", prop.contact.firstName);

		//multiple changes
		contact = new ContactDetailProp();
		contact.firstName = "Sathya";
		contact.lastName = "Thilakan";
		contact.mobilePhone = "+6598361844";
		contact.homePhone = "+6565227030";
		contact.officePhone = "+6563083013";
		Member.updateContactDetails(client, prop.memberId, contact, validUser);
		prop = MemberCore.safeGet(client, prop.memberId).toProp();
		assertEquals("Sathya", prop.contact.firstName);
		assertEquals("Thilakan", prop.contact.lastName);
		assertEquals("+6598361844", prop.contact.mobilePhone);
		assertEquals("+6565227030", prop.contact.homePhone);
		assertEquals("+6563083013", prop.contact.officePhone);

		//phone no cannot be invalid
		contact = new ContactDetailProp();
		contact.mobilePhone = "9384938ad";
		try {
			Member.updateContactDetails(client, prop.memberId, contact, validUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_RESOURCE_INCORRECTLY_SPECIFIED, ex.statusCode);
		}

		//email cannot be invalid
		contact = new ContactDetailProp();
		contact.email = "sathyafkdjf";
		try {
			Member.updateContactDetails(client, prop.memberId, contact, validUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_RESOURCE_INCORRECTLY_SPECIFIED, ex.statusCode);
		}

		//email cannot clash with another member
		contact = new ContactDetailProp();
		contact.firstName = "sathya isha";
		contact.email = "sathya.isha@gmail.com";
		Member.create(client, sgp.groupId, contact, validUser);

		contact = new ContactDetailProp();
		contact.email = "sathya.isha@gmail.com";
		try {
			Member.updateContactDetails(client, prop.memberId, contact, validUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_RESOURCE_ALREADY_EXISTS, ex.statusCode);
		}    	
	}

	@Test
	public void queryTest() {       	

		ContactDetailProp contact = new ContactDetailProp();
		contact.email = "Sathya.t@IshaFoundation.org";
		contact.mobilePhone = "+6598361844";
		contact.homePhone = "+6565227030";

		MemberProp sathya = Member.create(client, sgp.groupId, contact, User.SUPER_USER);
		assertEquals(1, sathya.memberId);

		try {
			Member.query("isha", "SathYa", null, null, null, invalidUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_INVALID_USER, ex.statusCode);
		}

		//search string cannot be less than 3 char
		try {
			Member.query("isha", "Sa", null, null, null, validUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_RESOURCE_INCORRECTLY_SPECIFIED, ex.statusCode);
		}

		List<MemberProp> searchResults = Member.query("isha", "SathYa", null, null, null, validUser);
		assertEquals(1, searchResults.size());

		searchResults = Member.query("isha", "sathyat", null, null, null, validUser);
		assertEquals(0, searchResults.size()); 

		//add few more members
		contact = new ContactDetailProp();
		contact.firstName = "Amit";
		contact.email = "amit@gmail.com";
		MemberProp amit = Member.create(client, sgp.groupId, contact, User.SUPER_USER);
		assertEquals(2, amit.memberId);

		contact = new ContactDetailProp();
		contact.firstName = "Shiva";
		contact.email = "sjawaji@gmail.com";
		MemberProp shiva = Member.create(client, sydney.groupId, contact, User.SUPER_USER);
		assertEquals(3, shiva.memberId);

		contact = new ContactDetailProp();
		contact.firstName = "Anantha";
		contact.email = "anantha@gmail.com";
		MemberProp anantha = Member.create(client, kl.groupId, contact, User.SUPER_USER);
		assertEquals(4, anantha.memberId);

		//search for gmail without specifying group
		searchResults = Member.query("isha", "Gmail", null, null, null, validUser);
		assertEquals(3, searchResults.size());
		//should be sorted by name
		assertEquals("Amit", searchResults.get(0).contact.firstName);
		assertEquals("Anantha", searchResults.get(1).contact.firstName);
		assertEquals("Shiva", searchResults.get(2).contact.firstName);

		//search for gmail only for sgp and kl
		Set<Long> groupIds = new HashSet<>();
		groupIds.add(sgp.groupId);
		groupIds.add(kl.groupId);    	
		searchResults = Member.query("isha", "Gmail", groupIds, null, null, validUser);
		assertEquals(2, searchResults.size());
		//should be sorted by name
		assertEquals("Amit", searchResults.get(0).contact.firstName);
		assertEquals("Anantha", searchResults.get(1).contact.firstName);   

		//search phone number
		searchResults = Member.query("isha", "844", groupIds, null, null, validUser); //matches sathya
		assertEquals(1, searchResults.size());
		assertEquals("sathya.t@ishafoundation.org", searchResults.get(0).name);   

		//create a shambhavi program and mystic eye program 
		//shambhavi;//i = Practice.create(client, "Shambhavi", User.SUPER_USER);
		PracticeProp mysticEyePractice = Practice.create(client, "Mystic Eye Pracice", User.SUPER_USER);
		Set<Long> practiceIds = new HashSet<>();
		practiceIds.add(shambhavi.practiceId);
		ProgramTypeProp ie7day = ProgramType.create(client, "IE 7 Day", practiceIds, User.SUPER_USER);

		practiceIds.clear();
		practiceIds.add(mysticEyePractice.practiceId);
		ProgramTypeProp mysticEye = ProgramType.create(client, "MysticEye1Day", practiceIds, User.SUPER_USER);
		VenueProp notFixed = Venue.create(client, "Not Fixed", "NA", sgp.groupId, User.SUPER_USER);
		TeacherProp notFixed2 = Teacher.create(client, "dummy@dummy.com", sgp.groupId, User.SUPER_USER);
		ProgramProp ie7day2011 = Program.create(client, sgp.groupId, ie7day.programTypeId, 
				notFixed.venueId, notFixed2.teacherId, 20110505, 20110511, 1, null, User.SUPER_USER);
		ProgramProp mysticEyeJan14 = Program.create(client, sgp.groupId, 
				mysticEye.programTypeId, notFixed.venueId, notFixed2.teacherId, 
				20140112, 20140112, 1, null, User.SUPER_USER);
		Member.addOrDeleteProgram(client, sathya.memberId, ie7day2011.programId, true, User.SUPER_USER);
		Member.addOrDeleteProgram(client, amit.memberId, ie7day2011.programId, true, User.SUPER_USER);
		Member.addOrDeleteProgram(client, shiva.memberId, ie7day2011.programId, true, User.SUPER_USER);
		Member.addOrDeleteProgram(client, anantha.memberId, ie7day2011.programId, true, User.SUPER_USER);

		//mystic eye only for amit
		Member.addOrDeleteProgram(client, amit.memberId, mysticEyeJan14.programId, true, User.SUPER_USER);

		//search for both mysticeye  - only amit
		practiceIds.add(shambhavi.practiceId);
		practiceIds.add(mysticEyePractice.practiceId);
		List<MemberProp> memberProps = Member.query(client, "amit", null, practiceIds, null, User.SUPER_USER);
		assertEquals(1, memberProps.size());

		//search for all members that are initiated into shambhavi
		practiceIds.clear();
		practiceIds.add(shambhavi.practiceId);
		memberProps = Member.query(client, null, null, practiceIds, null, User.SUPER_USER);
		System.out.println("memberProps = " + new Gson().toJson(memberProps));
		assertEquals(4, memberProps.size());

		//limit size to 1
		memberProps = Member.query(client, null, null, practiceIds, 1, User.SUPER_USER);
		assertEquals(1, memberProps.size());

		//limit to 4
		memberProps = Member.query(client, null, null, practiceIds, 4, User.SUPER_USER);
		assertEquals(4, memberProps.size());

	}

	@Test 
	public void safeGetTest() {
		ContactDetailProp contact = new ContactDetailProp();
		contact.email = "Sathya.t@IshaFoundation.org";
		contact.mobilePhone = "+6598361844";
		contact.homePhone = "+6565227030";

		MemberProp prop = Member.create(client, sgp.groupId, contact, User.SUPER_USER);

		try {
			Member.safeGet(client, prop.memberId, invalidUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals("invalid user cannot view member", Status.ERROR_INVALID_USER, ex.statusCode);
		}

		prop = Member.safeGet(client, prop.memberId, validUser).toProp();
		assertEquals("sathya.t@ishafoundation.org", prop.contact.email);
		assertEquals("+6598361844", prop.contact.mobilePhone);
		assertEquals("+6565227030", prop.contact.homePhone);
	}

	@Test
	public void addOrDeleteGroupTest() {
		ContactDetailProp contact = new ContactDetailProp();
		contact.email = "Sathya.t@IshaFoundation.org";
		contact.mobilePhone = "+6598361844";
		contact.homePhone = "+6565227030";

		MemberProp prop = Member.create(client, sgp.groupId, contact, User.SUPER_USER);

		//user cannot be invalid
		try {
			Member.addOrDeleteCenter(client, prop.memberId, 100, true, invalidUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_INVALID_USER, ex.statusCode);
		}

		//member should exist
		try {
			Member.addOrDeleteCenter(client, 3456, kl.groupId, true, validUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
		}

		//center should exist
		try {
			Member.addOrDeleteCenter(client, prop.memberId, 100, true, validUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
		}

		//add kl
		Member.addOrDeleteCenter(client, prop.memberId, kl.groupId, true, validUser);
		ObjectifyFilter.complete();
		prop = MemberCore.safeGet(client, prop.memberId).toProp();
		assertEquals(2, prop.groupIds.size());
		assertTrue(prop.groupIds.contains(sgp.groupId));
		assertTrue(prop.groupIds.contains(kl.groupId));

		//can add the same center again
		Member.addOrDeleteCenter(client, prop.memberId, kl.groupId, true, validUser);
		ObjectifyFilter.complete();
		prop = MemberCore.safeGet(client, prop.memberId).toProp();
		assertEquals(2, prop.groupIds.size());
		assertTrue(prop.groupIds.contains(sgp.groupId));
		assertTrue(prop.groupIds.contains(kl.groupId));

		//remove center
		Member.addOrDeleteCenter(client, prop.memberId, kl.groupId, false, validUser);
		ObjectifyFilter.complete();
		prop = MemberCore.safeGet(client, prop.memberId).toProp();
		assertEquals(1, prop.groupIds.size());
		assertTrue(prop.groupIds.contains(sgp.groupId));
		assertTrue(! prop.groupIds.contains(kl.groupId));

		//can remove center again
		Member.addOrDeleteCenter(client, prop.memberId, kl.groupId, false, validUser);
		ObjectifyFilter.complete();
		prop = MemberCore.safeGet(client, prop.memberId).toProp();
		assertEquals(1, prop.groupIds.size());
		assertTrue(prop.groupIds.contains(sgp.groupId));
		assertTrue(! prop.groupIds.contains(kl.groupId));

		//there should be atleast 1 center
		try {
			Member.addOrDeleteCenter(client, prop.memberId, sgp.groupId, false, validUser);        	
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_PRECONDITION_FAILED, ex.statusCode);
		}    	    	
	}

	@Test
	public void getAllForCenterTest() {
		//should be valid user
		try {
			Member.getAllForGroup(client, sgp.groupId, invalidUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_INVALID_USER, ex.statusCode);
		}

		List<MemberProp> memberProps = Member.getAllForGroup(client, sgp.groupId, validUser);
		assertEquals(0, memberProps.size());

		ContactDetailProp contact = new ContactDetailProp();
		contact.email = "Sathya.t@IshaFoundation.org";
		contact.mobilePhone = "+6598361844";
		contact.homePhone = "+6565227030";    	
		Member.create(client, sgp.groupId, contact, validUser);

		memberProps = Member.getAllForGroup(client, sgp.groupId, validUser);
		assertEquals(1, memberProps.size());

		//add 1 more
		contact = new ContactDetailProp();
		contact.email = "amit@gmail.com";
		contact.firstName = "Amit";    	
		Member.create(client, sgp.groupId, contact, validUser);

		//getAllForCenter should return in sorted order
		memberProps = Member.getAllForGroup(client, sgp.groupId, validUser);
		assertEquals(2, memberProps.size());    	
		assertEquals("Amit", memberProps.get(0).contact.firstName);
		assertEquals("sathya.t@ishafoundation.org", memberProps.get(1).contact.email);    	
	}

	@Test
	public void getAllInteractionsTest() throws InterruptedException {    	 
		ContactDetailProp contact = new ContactDetailProp();
		contact.email = "Sathya.t@IshaFoundation.org";
		contact.mobilePhone = "+6598361844";
		contact.homePhone = "+6565227030";    	
		long memberId = Member.create(client, sgp.groupId, contact, validUser).memberId;

		try {
			Member.getAllInteractions(invalidClient, memberId, validUser);
			assertTrue(false);    		    		
		} catch (APIException ex) {
			assertEquals("client should exist", Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
		}

		try {
			Member.getAllInteractions(client, memberId, invalidUser);
			assertTrue(false);    		    		
		} catch (APIException ex) {
			assertEquals("user should be valid", Status.ERROR_INVALID_USER, ex.statusCode);
		}

		try {
			Member.getAllInteractions(client, 100, validUser); //100 is an invalid member
			assertTrue(false);    		    		
		} catch (APIException ex) {
			assertEquals("member id should exist", Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
		}    	    	    

		//returns list with 0 elements if there are no interactions
		List<InteractionProp> interactions = 
				InteractionCore.get(client, memberId);
		assertEquals("getall works if there are no records", 0, interactions.size());

		//add some interactions
		Date date1 = new Date();
		Member.addInteraction(client, memberId, "interaction 1", 
				InteractionType.EMAIL, date1, validUser);

		Thread.sleep(100);
		Member.addInteraction(client, memberId, "interaction 2", 
				InteractionType.FB, new Date(), validUser2);    	
		Thread.sleep(100);
		Member.addInteraction(client, memberId, "interaction 3", 
				InteractionType.FB, new Date(), validUser);   

		//getAll should return in descending order of date
		interactions = Member.getAllInteractions(client, memberId, validUser);
		assertEquals("no of interactions returned by getall should match the number added", 
				3, interactions.size());

		assertEquals("getAll should return in descending order of date", 
				"interaction 3", interactions.get(0).interaction);
		assertEquals("getAll should return in descending order of date", 
				"interaction 2", interactions.get(1).interaction);
		assertEquals("getAll should return in descending order of date", 
				"interaction 1", interactions.get(2).interaction);

		assertEquals("getAll returns correct interaction", 
				"interaction 1", interactions.get(2).interaction);
		assertEquals("getAll returns correct interaction type", 
				InteractionType.EMAIL, interactions.get(2).interactionType);
		assertEquals("getAll returns correct date", 
				date1, interactions.get(2).timestamp);
		assertEquals("getAll returns correct user id", 
				UserCore.safeGet(client, validUser).toProp().userId, interactions.get(2).userId);
		assertEquals("interaction id is assigned in sequence", 
				1, interactions.get(2).interactionId);    	

		assertEquals("getAll returns correct user id", 
				UserCore.safeGet(client, validUser2).toProp().userId, interactions.get(1).userId);
	}

	@Test
	public void addInteractionTest() throws InterruptedException {
		//This test method also covers getAllInteractions

		ContactDetailProp contact = new ContactDetailProp();    	
		contact.email = "sathya.t@ishafoundation.org";
		MemberProp prop = Member.create(client, sgp.groupId, contact, validUser);

		try {
			Member.addInteraction(invalidClient, prop.memberId, 
					"test interaction", InteractionType.EMAIL, null, validUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals("client should be valid", Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					ex.statusCode);
		}

		try {
			Member.addInteraction(client, prop.memberId, 
					"test interaction", InteractionType.EMAIL, null, invalidUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals("client should be valid", Status.ERROR_INVALID_USER, 
					ex.statusCode);
		}

		Member.addInteraction(client, prop.memberId, 
				"test interaction", InteractionType.EMAIL, null, validUser);
		Thread.sleep(100);
		Member.addInteraction(client, prop.memberId, 
				"test interaction 2", InteractionType.PHONE, null, validUser);

		List<InteractionProp> interactionProps = 
				Member.getAllInteractions(client, prop.memberId, validUser);
		assertEquals("can save interaction", 2, interactionProps.size());
		assertEquals("interaction text saved", "test interaction 2", 
				interactionProps.get(0).interaction);
		assertEquals("interaction type saved", InteractionType.PHONE, 
				interactionProps.get(0).interactionType);

		assertEquals("interaction text saved", "test interaction", 
				interactionProps.get(1).interaction);
		assertEquals("interaction type saved", InteractionType.EMAIL, 
				interactionProps.get(1).interactionType);       	    	
	}

	@Test
	public void deleteInteractionTest() {
		ContactDetailProp contact = new ContactDetailProp();    	
		contact.email = "sathya.t@ishafoundation.org";
		MemberProp prop = Member.create(client, sgp.groupId, contact, validUser);

		InteractionProp interactionProp = Member.addInteraction(client, prop.memberId, 
				"test interaction", InteractionType.EMAIL, null, validUser);
		List<InteractionProp> interactionProps = 
				Member.getAllInteractions(client, prop.memberId, validUser);
		assertEquals(1, interactionProps.size());

		try {
			Member.deleteInteraction(invalidClient, prop.memberId, interactionProp.interactionId, 
					validUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals("client should be valid", Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					ex.statusCode);
		}

		//invalid member id
		try {
			Member.deleteInteraction(client, 100, interactionProp.interactionId, 
					validUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals("member id should be valid", Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					ex.statusCode);
		}

		//invalid interaction id
		try {
			Member.deleteInteraction(client, prop.memberId, 909, 
					validUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals("interaction id should be valid", Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					ex.statusCode);
		}

		//another user without permission cannot delete 
		try {
			Member.deleteInteraction(client, prop.memberId, interactionProp.interactionId, validUser2);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals("permission required", Status.ERROR_INSUFFICIENT_PERMISSION, 
					ex.statusCode);
		}

		Member.deleteInteraction(client, prop.memberId, interactionProp.interactionId, 
				validUser); 
		interactionProps = Member.getAllInteractions(client, prop.memberId, validUser);
		assertEquals("interaction deleted", 0, interactionProps.size());

		//user with permission can delete
		interactionProp = Member.addInteraction(client, prop.memberId, 
				"test interaction", InteractionType.EMAIL, null, validUser);
		interactionProps = Member.getAllInteractions(client, prop.memberId, validUser);
		assertEquals(1, interactionProps.size());

		Member.deleteInteraction(client, prop.memberId, interactionProp.interactionId, 
				userWithPermission); 
		interactionProps = Member.getAllInteractions(client, prop.memberId, validUser);
		assertEquals(0, interactionProps.size());

	}

	public void updateInteractionTest() {
		ContactDetailProp contact = new ContactDetailProp();    	
		contact.email = "sathya.t@ishafoundation.org";
		MemberProp prop = Member.create(client, sgp.groupId, contact, validUser);

		InteractionProp interactionProp = Member.addInteraction(client, prop.memberId, 
				"test interaction", InteractionType.EMAIL, null, validUser);
		List<InteractionProp> interactionProps = 
				Member.getAllInteractions(client, prop.memberId, validUser);
		assertEquals(1, interactionProps.size());

		try {
			Member.updateInteraction(invalidClient, interactionProp.interactionId, 
					"after update", null, null, validUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals("client should be valid", Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					ex.statusCode);
		}

		//invalid interaction id
		try {
			Member.updateInteraction(client, interactionProp.interactionId, "after update",
					null, null, validUser);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals("interaction id should be valid", Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					ex.statusCode);
		}

		//another user without permission cannot update 
		try {
			Member.updateInteraction(client, interactionProp.interactionId, null, null, null , validUser2);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals("permission required", Status.ERROR_INSUFFICIENT_PERMISSION, 
					ex.statusCode);
		}

		Member.updateInteraction(client, interactionProp.interactionId, "after update", 
				InteractionType.PHONE, null, validUser); 
		interactionProps = Member.getAllInteractions(client, prop.memberId, validUser);
		assertEquals(1, interactionProps.size());
		assertEquals("after update", "after update", interactionProps.get(0).interaction);
		assertEquals("after update", InteractionType.PHONE, interactionProps.get(0).interactionType);

		//user with permission can update
		interactionProp = Member.updateInteraction(client, 
				interactionProp.interactionId, "after update 2", 
				InteractionType.FACETOFACE, null, userWithPermission);
		interactionProps = Member.getAllInteractions(client, prop.memberId, validUser);
		assertEquals(1, interactionProps.size());
		assertEquals("after update", "after update 2", interactionProps.get(0).interaction);
		assertEquals("after update", InteractionType.FACETOFACE, interactionProps.get(0).interactionType); 	    	
	}

	@Test
	public void populateDependantIndexFieldsTest() {

		assertTrue(false);

		//    	MemberEntity memberEntity = new MemberEntity();
		//    	try {
		//    		MemberCore.populateDependantIndexFields(memberEntity);
		//    	} catch (APIException e) {
		//			assertEquals(Status.ERROR_RESOURCE_INCORRECTLY_SPECIFIED, e.statusCode);
		//		}
		//    	
		//    	//email specified but not name
		//    	memberEntity.email = "emailsathya@gmail.com";
		//    	MemberCore.populateDependantIndexFields(memberEntity);
		//    	assertEquals(memberEntity.name, memberEntity.email);
		//    	assertEquals(memberEntity.nameFirstChar, "e");
		//    	
		//    	//specify phone number but not email or name
		//    	memberEntity = new MemberEntity();
		//    	memberEntity.mobilePhone = "+6598361844";
		//    	MemberCore.populateDependantIndexFields(memberEntity);
		//    	assertEquals(memberEntity.name, memberEntity.mobilePhone);
		//    	assertEquals(memberEntity.nameFirstChar, "+");
		//    	
		//    	//specify name and email
		//    	memberEntity = new MemberEntity();
		//    	memberEntity.firstName = "Sathyanarayanan";
		//    	memberEntity.email = "sathya.t@ishafoundation.org";
		//    	MemberCore.populateDependantIndexFields(memberEntity);
		//    	assertEquals(memberEntity.name, "sathyanarayanan");
		//    	assertEquals(memberEntity.nameFirstChar, "s");    	    	  
	}

	@Test
	public void addOrDeleteProgramTest() {
		assertTrue(false);
	}

	@Test
	public void bulkUploadParticipantsTest() {
		assertEquals(true, false);
	}

	@Test
	public void bulkUpdateWithoutValidations() {
		assertTrue(false);
	}

	@Test public void bulkUploadProgramParticipants() {
		assertTrue(false);
	}

	@Test public void ensureValidTest() {
		assertTrue(false);    
	} 

	@Test public void addProgramToMembersTest() {
		ContactDetailProp contactDetailProp = new ContactDetailProp();
		contactDetailProp.email = "simansmile@yahoo.com";

		MemberProp narasimhan = Member.create(client, sgp.groupId, contactDetailProp, User.SUPER_USER);

		contactDetailProp.email = "thulasidhar@gmail.com";
		MemberProp thulasi = Member.create(client, sgp.groupId, contactDetailProp, User.SUPER_USER);

		VenueProp adiYogiAlayam = Venue.create(client, "Adi yogi aalayam", 
				"Adi yogi aalamay", iyc.groupId, User.SUPER_USER);

		PracticeProp suryaKriya = Practice.create(client, "surya kriya", User.SUPER_USER);
		PracticeProp upaYoga = Practice.create(client, "upa yoga", User.SUPER_USER);
		PracticeProp angamardhana = Practice.create(client, "angamardhana", User.SUPER_USER);
		PracticeProp yogaAsanas = Practice.create(client, "yoga asanas", User.SUPER_USER);

		Set<Long> practiceIds = new HashSet<Long>();
		practiceIds.add(suryaKriya.practiceId);
		practiceIds.add(upaYoga.practiceId);
		practiceIds.add(angamardhana.practiceId);
		practiceIds.add(yogaAsanas.practiceId);

		ProgramTypeProp hatayoga21day = ProgramType.create(client, 
				"Hata yoga 21 day", practiceIds, User.SUPER_USER);

		TeacherProp sheela = Teacher.create(client, "sheela.r@ishafoundation.org", iyc.groupId, 
				User.SUPER_USER);

		ProgramProp hatayoga21day2013 = Program.create(client, 
				iyc.groupId, hatayoga21day.programTypeId, adiYogiAlayam.venueId, 
				sheela.teacherId, 20130701, 20130721, 1, null, User.SUPER_USER);

		List<MemberEntity> memberEntities = new ArrayList<MemberEntity>();
		memberEntities.add(null);
		memberEntities.add(null);

		//can call with null members
		Member.addProgramToMembers(client, memberEntities, hatayoga21day2013.programId);

		memberEntities = new ArrayList<MemberEntity>();
		memberEntities.add(Member.safeGet(client, narasimhan.memberId, User.SUPER_USER));
		memberEntities.add(Member.safeGet(client, thulasi.memberId, User.SUPER_USER));
		memberEntities.add(null);

		Member.addProgramToMembers(client, memberEntities, hatayoga21day2013.programId);

		//clear session cache
		ObjectifyFilter.complete();
		OfyService.ofy(client).clear();

		MemberProp thulasiProp = Member.safeGet(client, thulasi.memberId, User.SUPER_USER).toProp();    	    	
		assertEquals(1, thulasiProp.programIds.size());
		assertTrue(thulasiProp.programIds.contains(hatayoga21day2013.programId));
		assertEquals(1, thulasiProp.programTypeIds.size());
		assertTrue(thulasiProp.programTypeIds.contains(hatayoga21day.programTypeId));
		assertEquals(4, thulasiProp.practiceIds.size());
		assertTrue(thulasiProp.practiceIds.contains(suryaKriya.practiceId));
		assertTrue(thulasiProp.practiceIds.contains(upaYoga.practiceId));
		assertTrue(thulasiProp.practiceIds.contains(angamardhana.practiceId));
		assertTrue(thulasiProp.practiceIds.contains(yogaAsanas.practiceId));

		MemberProp narasimhanProp = 
				Member.safeGet(client, narasimhan.memberId, User.SUPER_USER).toProp();
		assertEquals(1, narasimhanProp.programIds.size());
		assertTrue(narasimhanProp.programIds.contains(hatayoga21day2013.programId));
		assertEquals(1, narasimhanProp.programTypeIds.size());
		assertTrue(narasimhanProp.programTypeIds.contains(hatayoga21day.programTypeId));
		assertEquals(4, narasimhanProp.practiceIds.size());
		assertTrue(narasimhanProp.practiceIds.contains(suryaKriya.practiceId));
		assertTrue(narasimhanProp.practiceIds.contains(upaYoga.practiceId));
		assertTrue(narasimhanProp.practiceIds.contains(angamardhana.practiceId));
		assertTrue(narasimhanProp.practiceIds.contains(yogaAsanas.practiceId)); 

		//exception if client is invalid
		try {
			Member.addProgramToMembers(invalidClient, memberEntities, hatayoga21day2013.programId);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
		}

		//program id should be valid
		try {
			Member.addProgramToMembers(client, memberEntities, 938); //invalid program id
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
		}
	}

	@Test public void bulkCreateWithoutValidationsTest() {

		ContactDetailProp thulasi = new ContactDetailProp();
		thulasi.email = "thulasidhar@gmail.com";
		thulasi.firstName = "Thulasidhar";
		thulasi.lastName = "Kosalram";
		thulasi.mobilePhone = "+6592705371";  

		ContactDetailProp narasimhan = new ContactDetailProp();
		narasimhan.email = "simansmile@yahoo.com";
		narasimhan.firstName = "Narasimhan";
		narasimhan.mobilePhone = "+6597302205";

		VenueProp adiYogiAlayam = Venue.create(client, "Adi yogi aalayam", 
				"Adi yogi aalamay", iyc.groupId, User.SUPER_USER);

		PracticeProp suryaKriya = Practice.create(client, "surya kriya", User.SUPER_USER);
		PracticeProp upaYoga = Practice.create(client, "upa yoga", User.SUPER_USER);
		PracticeProp angamardhana = Practice.create(client, "angamardhana", User.SUPER_USER);
		PracticeProp yogaAsanas = Practice.create(client, "yoga asanas", User.SUPER_USER);

		Set<Long> practiceIds = new HashSet<Long>();
		practiceIds.add(suryaKriya.practiceId);
		practiceIds.add(upaYoga.practiceId);
		practiceIds.add(angamardhana.practiceId);
		practiceIds.add(yogaAsanas.practiceId);

		ProgramTypeProp hatayoga21day = ProgramType.create(client, 
				"Hata yoga 21 day", practiceIds, User.SUPER_USER);

		TeacherProp sheela = Teacher.create(client, "sheela.r@ishafoundation.org", iyc.groupId, 
				User.SUPER_USER);

		ProgramProp hatayoga21day2013 = Program.create(client, 
				iyc.groupId, hatayoga21day.programTypeId, adiYogiAlayam.venueId, 
				sheela.teacherId, 20130701, 20130721, 1, null, User.SUPER_USER);

		List<ContactDetailProp> contactDetailProps = new ArrayList<ContactDetailProp>();
		contactDetailProps.add(null);
		contactDetailProps.add(null);

		Member.bulkCreateWithoutValidations(client, contactDetailProps, hatayoga21day2013.programId);

		contactDetailProps.clear();
		contactDetailProps.add(null);
		contactDetailProps.add(thulasi);
		contactDetailProps.add(null);
		contactDetailProps.add(narasimhan);
		contactDetailProps.add(null);


		List<Long> memberIds = 
				Member.bulkCreateWithoutValidations(client, contactDetailProps, hatayoga21day2013.programId);
		assertEquals(5, memberIds.size());
		//same order should be preserved
		assertEquals(null, memberIds.get(0));
		assertEquals(1, memberIds.get(1) + 0);
		assertEquals(null, memberIds.get(2));
		assertEquals(2, memberIds.get(3) + 0);
		assertEquals(null, memberIds.get(4));

		//clear cache
		ObjectifyFilter.complete();
		OfyService.ofy(client).clear();

		MemberProp thulasiProp = Member.safeGet(client, 1, User.SUPER_USER).toProp();
		assertEquals("thulasidhar@gmail.com", thulasiProp.contact.email);
		assertEquals("Thulasidhar Kosalram", thulasiProp.contact.firstName + " " + thulasiProp.contact.lastName);
		assertEquals("+6592705371", thulasiProp.contact.mobilePhone);
		assertEquals(1, thulasiProp.programIds.size());
		assertTrue(thulasiProp.programIds.contains(hatayoga21day2013.programId));
		assertEquals(1, thulasiProp.programTypeIds.size());
		assertTrue(thulasiProp.programTypeIds.contains(hatayoga21day.programTypeId));
		assertEquals(4, thulasiProp.practiceIds.size());
		assertTrue(thulasiProp.practiceIds.contains(suryaKriya.practiceId));
		assertTrue(thulasiProp.practiceIds.contains(upaYoga.practiceId));
		assertTrue(thulasiProp.practiceIds.contains(yogaAsanas.practiceId));
		assertTrue(thulasiProp.practiceIds.contains(angamardhana.practiceId));

		List<MemberProp> matches = Member.query(client, "thulasidhar", null, null, null, validUser);
		assertEquals(1, matches.size());
		assertEquals(thulasiProp.memberId, matches.get(0).memberId);

		MemberProp narasimhanProp = Member.safeGet(client, 2, User.SUPER_USER).toProp();
		assertEquals("simansmile@yahoo.com", narasimhanProp.contact.email);
		assertEquals("Narasimhan", narasimhanProp.contact.firstName);
		assertEquals("+6597302205", narasimhanProp.contact.mobilePhone);
		assertEquals(1, narasimhanProp.programIds.size());
		assertTrue(narasimhanProp.programIds.contains(hatayoga21day2013.programId));
		assertEquals(1, narasimhanProp.programTypeIds.size());
		assertTrue(narasimhanProp.programTypeIds.contains(hatayoga21day.programTypeId));
		assertEquals(4, narasimhanProp.practiceIds.size());
		assertTrue(narasimhanProp.practiceIds.contains(suryaKriya.practiceId));
		assertTrue(narasimhanProp.practiceIds.contains(upaYoga.practiceId));
		assertTrue(narasimhanProp.practiceIds.contains(yogaAsanas.practiceId));
		assertTrue(narasimhanProp.practiceIds.contains(angamardhana.practiceId));


		//exception if client is invalid
		try {
			Member.bulkCreateWithoutValidations(invalidClient, 
					contactDetailProps, hatayoga21day2013.programId);
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
		}

		//program id should be valid
		try {
			Member.bulkCreateWithoutValidations(client, contactDetailProps, 992); //invalid program id
			assertTrue(false);
		} catch (APIException ex) {
			assertEquals(Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
		}
	}

	@Test public void getMatchingMemberIdsTest() {
		ContactDetailProp contactDetailProp = new ContactDetailProp();    
		List<ContactDetailProp> contactDetailProps = new ArrayList<>();

		List<Long> matching = Member.getMatchingMemberIds(client, contactDetailProps);
		assertEquals(0, matching.size());

		contactDetailProp.email = "sathya.t@ishafoundation.org";
		matching = Member.getMatchingMemberIds(client, contactDetailProps);
		assertEquals(0, matching.size());   

		ContactDetailProp c = new ContactDetailProp();
		c.firstName = "sathyanarayanan";
		c.email = "sathya.t@ishafoundation.org";
		c.mobilePhone = "+6593232152";    	
		MemberProp sathya = Member.create(client, sgp.groupId, c, User.SUPER_USER);

		c = new ContactDetailProp();
		c.firstName = "Thulasidhar";
		c.email = "thulasidhar@gmail.com";
		MemberProp thulasi = Member.create(client, sgp.groupId, c, User.SUPER_USER);

		c = new ContactDetailProp();
		c.firstName = "Sharmila";
		c.lastName = "Napa";
		c.email = "sharmila@bhairaviyoga.sg";
		MemberProp sharmila = Member.create(client, sgp.groupId, c, User.SUPER_USER);    	   

		//create contacts
		ContactDetailProp syamala = new ContactDetailProp();
		syamala.firstName = "syamala";
		syamala.mobilePhone = "+6593232152";

		ContactDetailProp thilakan = new ContactDetailProp();
		thilakan.firstName = "thilakan";
		thilakan.email = "rthilakan@gmail.com";

		ContactDetailProp sowmya = new ContactDetailProp();
		sowmya.firstName = "sowmya";
		sowmya.email = "sathya.t@ishafoundation.org";

		ContactDetailProp sathyaContactDetail = new ContactDetailProp();
		sathyaContactDetail.firstName = "sathya";    	
		sathyaContactDetail.mobilePhone = "+6593232152";

		contactDetailProps = new ArrayList<>();
		contactDetailProps.add(syamala);
		contactDetailProps.add(thilakan);
		contactDetailProps.add(sowmya);
		contactDetailProps.add(sathyaContactDetail);

		List<Long> matchingMemberIds = Member.getMatchingMemberIds(client, contactDetailProps);
		assertEquals(4, matchingMemberIds.size());
		assertEquals(null, matchingMemberIds.get(0));
		assertEquals(null, matchingMemberIds.get(1));
		assertEquals(null, matchingMemberIds.get(2));
		assertEquals(new Long(sathya.memberId), matchingMemberIds.get(3));    	    	    	    
	}

	@Test public void saveUnverifiedProgramsTest() {
		//create members
		ContactDetailProp c = new ContactDetailProp(); 
		c.email = "thulasidhar@gmail.com";
		thulasi = Member.create("isha", sgp.groupId, c, User.SUPER_USER);
		assertEquals(1, thulasi.memberId);
		Member.addOrDeleteProgram("isha", thulasi.memberId, mysticEye201401.programId, true, 
				User.SUPER_USER);
		
		List<UnverifiedProgramProp> props = new ArrayList<>();
		UnverifiedProgramProp prop = new UnverifiedProgramProp();
		prop.programTypeId = shambhavi2Day.programTypeId;
		prop.city = "Bangalore";
		prop.country = "India";
		prop.dateYYYYMM = 200901;		
		props.add(prop);
		
		Member.saveUnverifiedPrograms(client, thulasi.memberId, props, User.SUPER_USER);
		
		thulasi = Member.safeGet(client, thulasi.memberId, User.SUPER_USER).toProp();
		assertEquals(1,thulasi.programIds.size());
		assertTrue(thulasi.programIds.contains(mysticEye201401.programId));
		assertEquals(2,thulasi.programTypeIds.size());
		assertTrue(thulasi.programTypeIds.contains(mysticEye1Day.programTypeId));
		assertTrue(thulasi.programTypeIds.contains(shambhavi2Day.programTypeId));
		
		
		

	}

	@Test public void getUnverifiedProgramsTest() {
		assertTrue(false);
	}

	@Test
	public void searchByProgramTypeTest() {
		assertTrue(false);
	}

	@Test
	public void searchByProgramTest() {
		assertTrue(false);
	}

	@Test
	public void searchByPracticeTest() {
		assertTrue(false);
	}

	@Test
	public void searchByPractice2Test() {
		assertTrue(false);
	}
}
